package com.bkgy.modularbx.slip.service.imp;

import com.alibaba.fastjson.JSON;
import com.bkgy.modularbx.slip.entity.*;
import com.bkgy.modularbx.slip.mapper.SlipDataRepository;
import com.bkgy.modularbx.slip.service.SlipDetectionService;
import com.bkgy.modularbx.slip.utils.MathUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 轧辊打滑检测服务实现类，负责分析轧辊运行数据并检测打滑事件。
 * 该服务通过分析轧辊的速度、扭矩和张力等参数，实时监测轧辊的运行状态，
 * 当发现异常情况时（如打滑或扭矩异常）会生成相应的告警信息。
 *
 * 主要功能包括：
 * - 实时分析轧辊运行数据，检测打滑事件
 * - 记录和保存打滑事件信息
 * - 维护打滑检测阈值参数
 * - 支持多轧辊并行监控
 *
 * @author Trae
 * @version 1.0
 * @see com.bkgy.modularbx.slip.entity.RollData
 * @see com.bkgy.modularbx.slip.entity.SlipEvent
 * @see com.bkgy.modularbx.slip.entity.SlipAnalysisResult
 * @see DataBufferService
 */
@Slf4j
@Service
public class SlipDetectionServiceImpl implements SlipDetectionService {

    /** 轧辊映射配置，包含监控参数和阈值设置 */
    private final RollMappingConfiguration config;

    /** 数据缓冲服务，用于获取轧辊运行数据 */
    private final DataBufferService dataBufferService;

    /** 打滑数据持久化仓库 */
    private final SlipDataRepository slipDataRepository;

    /** 记录每个轧辊最后一次打滑检测的时间 */
    private final Map<String, LocalDateTime> lastSlipDetectionMap = new HashMap<>();

    /** 打滑检测阈值参数映射 */
    private final Map<String, Float> slipThresholds;

    @Autowired
    public SlipDetectionServiceImpl(
            RollMappingConfiguration config,
            DataBufferService dataBufferService,
            SlipDataRepository slipDataRepository) {
        this.config = config;
        this.dataBufferService = dataBufferService;
        this.slipDataRepository = slipDataRepository;

        // 初始化阈值
        this.slipThresholds = new HashMap<>();
        Map<String, Double> thresholds = config.getSlipDetectionThresholds();
        for (Map.Entry<String, Double> entry : thresholds.entrySet()) {
            slipThresholds.put(entry.getKey(), entry.getValue().floatValue());
        }
    }

    /**
     * 分析轧辊数据并检测是否发生打滑。
     * 该方法通过分析轧辊的速度、扭矩和张力等参数，计算多个特征指标，
     * 并根据预设的阈值判断是否发生打滑。分析指标包括：
     * - 速度偏差（最大值和平均值）
     * - 相邻轧辊速度差
     * - 速度下降趋势
     * - 扭矩变化
     * - 张力状态
     *
     * @param rollDataList 待分析的轧辊数据列表
     * @return 分析结果对象，包含各项指标和打滑判定结果；如果输入数据无效则返回null
     */
    @Override
    public SlipAnalysisResult analyzeSlip(List<RollData> rollDataList) {
        if (rollDataList == null || rollDataList.isEmpty()) {
            return null;
        }

        RollData firstData = rollDataList.get(0);
        String rollId = firstData.getRollId();
        String displayName = firstData.getDisplayName();
        String section = firstData.getSection();
        int dataSize = rollDataList.size();

        // 提取数据数组
        float[] speedAct = new float[dataSize];//轧辊实际运行速度
        float[] speedSet = new float[dataSize];//轧辊设定速度（米/分）
        float[] masterSpeed = new float[dataSize];//机架主传动速度
        float[] torqueAct = new float[dataSize];//轧辊实际扭矩
        float[] tensionAct = new float[dataSize];//轧辊实际张力
        float[] speedDev = new float[dataSize];//计算轧辊实际速度与设定速度之间的绝对偏差值
        float[] timeIndices = new float[dataSize];

        for (int i = 0; i < dataSize; i++) {
            RollData data = rollDataList.get(i);
            //轧辊实际运行速度（米/分）
            speedAct[i] = data.getSpeedAct();
            //speedSet[i] = Float.isNaN(data.getSpeedSet()) ? data.getMasterSpeed() : data.getSpeedSet();
            speedSet[i] = data.getSpeedSet();// todo 设定速度 可能不存在吗？
            masterSpeed[i] = data.getMasterSpeed();//机架主传动速度
            torqueAct[i] = data.getTorqueAct();//轧辊实际扭矩
            tensionAct[i] = data.getTensionAct();//轧辊实际张力
            speedDev[i] = data.getSpeedDev();//轧辊实际速度与设定速度之间的绝对偏差值
            timeIndices[i] = i * 15.0f / dataSize; // 归一化时间索引
        }

        // 计算速度偏差最大值
        float speedDevAbsMax = MathUtils.findMax(speedDev);

        // 计算速度偏差 平均值
        float speedDevAbsMean = MathUtils.calculateMean(speedDev);

        // 计算 实际速度平均值
        float speedActMean = MathUtils.calculateMean(speedAct);

        // 计算扭矩方差
        float torqueVar = (float) MathUtils.calculateVariance(torqueAct);

        // 计算张力平均值
        float tensionActMean = MathUtils.calculateMean(tensionAct);

        // 计算速度下降比例
        float speedActDownPercent = MathUtils.calculateDownwardTrend(speedAct);

        //获取阀值设定值
        float speedDevThr = slipThresholds.getOrDefault("speedDevThr", 0.06f);
        // 计算速度偏差超过阈值的比例
        float speedDevPercent = MathUtils.calculateThresholdExceedRatio(speedDev, speedDevThr);

        // 计算线性回归斜率
        float kMasterSlope = (float) MathUtils.calculateSlope(timeIndices, masterSpeed);
        float kActSlope = (float) MathUtils.calculateSlope(timeIndices, speedAct);
        float kDev = Math.abs(kMasterSlope - kActSlope);

        // 与前辊平均速度差
        float speedMeanDevAbsFront = 0;
        //与后辊的平均速度差
        float speedMeanDevAbsRear = 0;

        // 尝试找到前一个轧辊的数据
        try {
            int position = firstData.getPosition();
            for (RollMapping mapping : config.getRollMappings()) {
                // 如果当前轧辊的position与前一个轧辊的position相等，则找到前一个轧辊的数据
                if (mapping.getPosition() == position - 1) {
                    // 获取前一个轧辊的数据
                    List<RollData> previousRollData = dataBufferService.getRecentData(mapping.getRollId());
                    if (!previousRollData.isEmpty()) {
                        // 创建一个数组，用于存储前一个轧辊的数据
                        float[] previousSpeedAct = new float[previousRollData.size()];
                        for (int i = 0; i < previousRollData.size(); i++) {
                            previousSpeedAct[i] = previousRollData.get(i).getSpeedAct();
                        }
                        //计算前一个轧辊的平均速度
                        float previousSpeedActMean = MathUtils.calculateMean(previousSpeedAct);
                        //计算当前轧辊与前一个轧辊的平均速度之差
                        speedMeanDevAbsFront = Math.abs(speedActMean - previousSpeedActMean);
                    }
                    break;
                }
            }

            // 尝试找到后一个轧辊的数据
            for (RollMapping mapping : config.getRollMappings()) {
                // 如果当前轧辊的position与后一个轧辊的position相等，则找到后一个轧辊的数据
                if (mapping.getPosition() == position + 1) {
                    // 获取后一个轧辊的数据
                    List<RollData> nextRollData = dataBufferService.getRecentData(mapping.getRollId());
                    if (!nextRollData.isEmpty()) {
                        // 创建一个数组，用于存储后一个轧辊的数据
                        float[] nextSpeedAct = new float[nextRollData.size()];
                        for (int i = 0; i < nextRollData.size(); i++) {
                            nextSpeedAct[i] = nextRollData.get(i).getSpeedAct();
                        }
                        //计算后一个轧辊的平均速度
                        float nextSpeedActMean = MathUtils.calculateMean(nextSpeedAct);
                        //计算当前轧辊与后一个轧辊的平均速度之差
                        speedMeanDevAbsRear = Math.abs(speedActMean - nextSpeedActMean);
                    }
                    break;
                }
            }
        } catch (Exception e) {
            log.warn("Error calculating adjacent roll speed differences: {}", e.getMessage());
        }

        // 创建分析结果对象
        SlipAnalysisResult result = new SlipAnalysisResult();
        result.setRollId(rollId);
        result.setDisplayName(displayName);
        result.setSection(section);
        result.setTimestamp(LocalDateTime.now());
        result.setSpeedDevAbsMax(speedDevAbsMax);// 速度偏差最大值
        result.setSpeedDevAbsMean(speedDevAbsMean);//  速度偏差平均值
        result.setSpeedMeanDevAbsFront(speedMeanDevAbsFront);//轧制前段的平均速度偏差绝对值
        result.setSpeedMeanDevAbsRear(speedMeanDevAbsRear);//轧制后段的平均速度偏差绝对值
        result.setKSlope(kActSlope);// 实际速度与主传动速度的斜率
        result.setKMasterSlope(kMasterSlope);// 主传动速度与实际速度的斜率
        result.setKDev(kDev);// 实际速度与主传动速度的斜率之差
        result.setSpeedActDownPercent(speedActDownPercent);// 实际速度下降的比例
        result.setSpeedDevPercent(speedDevPercent);// 速度偏差超过阈值的比例
        result.setTorqueVar(torqueVar);//扭矩数据的方差值（千牛米²），反映扭矩波动的剧烈程度
        result.setTensionActMean(tensionActMean);//实际张力的平均值（千牛），用于评估轧制过程中的张力状态
        // 判断是否出现打滑
        boolean isSlipping = determineSlipping(result);
        result.setSlipping(isSlipping);

        // 设置报警类型
        if (isSlipping) {
            result.setAlarmType(1); // 打滑报警
        } else if (torqueVar > slipThresholds.getOrDefault("torqueVarThr", 100.0f)
                && tensionActMean < 0) {
            result.setAlarmType(2); // 扭矩异常
        } else {
            result.setAlarmType(0); // 无报警
        }
        log.info("打滑分析结果：{}", JSON.toJSONString(result));
        return result;
    }

    /**
     * 根据分析结果判断是否发生打滑。
     * 该方法综合考虑多个指标，通过预设的阈值进行判断：
     * - 相邻轧辊速度差异
     * - 速度下降趋势
     * - 速度偏差比例
     * - 速度变化斜率
     *
     * @param result 包含各项分析指标的结果对象
     * @return 是否判定为打滑状态
     */
    private boolean determineSlipping(SlipAnalysisResult result) {
        // 判断打滑条件
        float speedMeanDevAbsFrReThr = slipThresholds.getOrDefault("speedMeanDevAbsFrReThr", 100.0f);
        float speedActPercentThr = slipThresholds.getOrDefault("speedActPercentThr", 0.1f);
        float speedDevPercentThr = slipThresholds.getOrDefault("speedDevPercentThr", 0.1f);
        float kDevThr = slipThresholds.getOrDefault("kDevThr", 0.5f);
        float speedDevAbsMaxThr = slipThresholds.getOrDefault("speedDevAbsMaxThr", 30.0f);
        float speedDevAbsMeanThr = slipThresholds.getOrDefault("speedDevAbsMeanThr", 15.0f);

        // 打滑检测逻辑：根据多个指标进行综合判断
        return ((result.getSpeedMeanDevAbsFront() > speedMeanDevAbsFrReThr ||
                result.getSpeedMeanDevAbsRear() > speedMeanDevAbsFrReThr) &&
                (result.getSpeedActDownPercent() > speedActPercentThr ||
                        result.getSpeedDevPercent() > speedDevPercentThr) &&
                result.getKDev() < kDevThr) ||
                ((result.getSpeedDevAbsMax() > speedDevAbsMaxThr &&
                        result.getSpeedDevAbsMean() > speedDevAbsMeanThr) &&
                        (result.getSpeedActDownPercent() > speedActPercentThr));
    }

    /**
     * 保存打滑事件记录。
     * 将检测到的打滑事件持久化到数据库中，用于后续分析和统计。
     *
     * @param slipEvent 待保存的打滑事件对象
     */
    @Override
    public void saveSlipEvent(SlipEvent slipEvent) {
        try {
            slipDataRepository.save(slipEvent);
        } catch (Exception e) {
            log.error("Error saving slip event: {}", e.getMessage(), e);
        }
    }

    /**
     * 获取指定轧辊的打滑事件记录。
     *
     * @param rollId 轧辊唯一标识符
     * @return 该轧辊的所有打滑事件记录列表
     */
    @Override
    public List<SlipEvent> getSlipEventsByRollId(String rollId) {
        return slipDataRepository.findByRollId(rollId);
    }

    /**
     * 获取指定轧辊在当天的打滑事件数量。
     *
     * @param rollId 轧辊唯一标识符
     * @param category 打滑类别（可选）
     * @return 打滑事件计数
     */
    @Override
    public long getSlipCountForRoll(String rollId, String category) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime startOfDay = now.toLocalDate().atStartOfDay();
        return slipDataRepository.countByRollIdAndTimestampBetween(rollId, startOfDay, now);
    }


    /**
     * 执行打滑分析任务。
     * 该方法定期对所有监控的轧辊进行打滑检测，优先分析主要监控的轧辊，
     * 然后再分析其他轧辊。
     */
    //@Scheduled(fixedDelayString = "${slip.detection.analysis.interval:2000}")
    public void performSlipAnalysis() {
        List<String> monitoredRolls = config.getMonitoringConfig().getPrimaryMonitoredRolls();

        // 可以先分析主要监控的轧辊
        for (String rollId : monitoredRolls) {
            analyzeRollSlip(rollId);
        }

        // 然后分析其他轧辊
        for (RollMapping mapping : config.getRollMappings()) {
            String rollId = mapping.getRollId();
            if (!monitoredRolls.contains(rollId)) {
                analyzeRollSlip(rollId);
            }
        }
    }

    /**
     * 分析单个轧辊的打滑状态。
     * 该方法获取轧辊的最新数据进行分析，如果检测到打滑且满足告警时间间隔要求，
     * 则创建并保存打滑事件记录。
     *
     * @param rollId 待分析的轧辊ID
     */
    private void analyzeRollSlip(String rollId) {
        try {
            List<RollData> recentData = dataBufferService.getRecentData(rollId);

            // 只有当有足够的数据点时才进行分析
            if (recentData.size() >= 10) {  // 至少需要10个数据点
                SlipAnalysisResult result = analyzeSlip(recentData);

                // 防止过于频繁地报同一个轧辊的告警
                LocalDateTime lastSlipTime = lastSlipDetectionMap.get(rollId);
                LocalDateTime now = LocalDateTime.now();

                // 如果检测到打滑，且与上次检测间隔至少30秒
                if (result.isSlipping() && (lastSlipTime == null ||
                        lastSlipTime.plusSeconds(30).isBefore(now))) {

                    SlipEvent event = createSlipEvent(rollId, recentData, result);
                    saveSlipEvent(event);

                    // 更新最后检测时间
                    lastSlipDetectionMap.put(rollId, now);

                    log.info("Slip detected on roll {}: {}", result.getDisplayName(), result);
                }
            }
        } catch (Exception e) {
            log.error("Error analyzing slip for roll {}: {}", rollId, e.getMessage(), e);
        }
    }

    /**
     * 创建打滑事件记录。
     * 根据分析结果和原始数据创建完整的打滑事件记录，包含：
     * - 基本信息（轧辊ID、显示名称、区域等）
     * - 分析结果数据
     * - 原始运行数据（速度、扭矩、张力等）
     *
     * @param rollId 轧辊ID
     * @param recentData 最近的轧辊运行数据
     * @param result 打滑分析结果
     * @return 创建的打滑事件记录
     */
    private SlipEvent createSlipEvent(String rollId, List<RollData> recentData, SlipAnalysisResult result) {
        SlipEvent event = new SlipEvent();
        event.setRollId(rollId);
        event.setDisplayName(result.getDisplayName());
        event.setSection(result.getSection());
        event.setTimestamp(LocalDateTime.now());
        event.setAnalysisResult(result);

        // 提取原始数据
        List<Float> masterSpeedData = new ArrayList<>();
        List<Float> rollSpeedActData = new ArrayList<>();
        List<Float> rollSpeedSetData = new ArrayList<>();
        List<Float> torquesActData = new ArrayList<>();
        List<Float> devSpeedData = new ArrayList<>();
        List<Float> tensionActData = new ArrayList<>();

        for (RollData data : recentData) {
            masterSpeedData.add(data.getMasterSpeed());
            rollSpeedActData.add(data.getSpeedAct());
            rollSpeedSetData.add(data.getSpeedSet());
            torquesActData.add(data.getTorqueAct());
            devSpeedData.add(data.getSpeedDev());
            tensionActData.add(data.getTensionAct());
        }

        event.setMasterSpeedData(masterSpeedData);
        event.setRollSpeedActData(rollSpeedActData);
        event.setRollSpeedSetData(rollSpeedSetData);
        event.setTorquesActData(torquesActData);
        event.setDevSpeedData(devSpeedData);
        event.setTensionActData(tensionActData);

        return event;
    }

    /**
     * 获取所有被监控的轧辊列表。
     *
     * @return 所有监控轧辊的ID列表
     */
    @Override
    public List<String> getAllMonitoredRolls() {
        return Collections.emptyList();
    }

    /**
     * 获取主要监控的轧辊列表。
     *
     * @return 主要监控轧辊的ID列表
     */
    @Override
    public List<String> getPrimaryMonitoredRolls() {
        return Collections.emptyList();
    }

    /**
     * 触发指定轧辊的打滑分析。
     *
     * @param rollId 待分析的轧辊ID
     * @return 分析结果对象
     */
    @Override
    public SlipAnalysisResult triggerAnalysis(String rollId) {
        return null;
    }

    /**
     * 更新打滑检测阈值参数。
     *
     * @param thresholdName 阈值参数名称
     * @param value 新的阈值值
     */
    @Override
    public void updateThreshold(String thresholdName, float value) {

    }
}